/*Yipeng Huang and Scott Rogowski*/
/*yh2315 smr2167*/

//The initial and first stage of the MIPS pipeline.  
module FetchStageWithRegisters ( /*AUTOARG*/
   // Outputs
   inst_o_valid, inst_o_addr, inst_delay_exception, pc_plus_four,
   instruction,
   // Inputs
   clk, reset, branch_address, jump_address, program_counter_source,
   jump, inst_i_valid, inst_i, bubble, pause

   );
	
	// basic inputs
	input clk, reset;
	
	// instructions inputs from Instruction Memory
	input inst_i_valid; // TODO: Analyze
	
	// instruction outputs to Instruction Memory
	output [31:0] inst_o_addr;
	
	input bubble;
	input pause;
		
	///////////////////////////////////////////////////////////
	//////////////////*PROGRAM COUNTER LOGIC*//////////////////
	///////////////////////////////////////////////////////////
	
	input [31:0] branch_address;
	input [31:0] jump_address;
	input program_counter_source; // whether or not to branch
	input jump; // whether or not to jump
	
	wire [31:0] pc_plus_four_mid;
	wire [31:0] four;
	assign four = 4;
	Adder pc_plus_four_add (.add_input_0(inst_o_addr), .add_input_1(four), .add_result(pc_plus_four_mid));
	
	wire [31:0] counter_or_branch_mid; // For temporarily holding the selected address between branch address and the naturally incremented address
	TwoToOneMultiplexer pc_branch_mux (.mux_in_0(pc_plus_four_mid), .mux_in_1(branch_address), .selector(program_counter_source), .mux_out(counter_or_branch_mid));
	
	wire [31:0] counter_or_branch_or_jump_mid;
	TwoToOneMultiplexer pc_jump_mux (.mux_in_0(counter_or_branch_mid), .mux_in_1(jump_address), .selector(jump), .mux_out(counter_or_branch_or_jump_mid));
	
/*	DelayRegisterFile pc_file (	.clk(clk), .reset(reset),
								.pause(!program_counter_source & !jump & !inst_i_valid), .data(counter_or_branch_or_jump_mid), .q(inst_o_addr));
*/	DelayRegisterFile pc_file (	.clk(clk), .reset(reset),
								.pause(1'b0), .data(counter_or_branch_or_jump_mid), .q(inst_o_addr));
								
								// PC should not advance if any of branch, jump, or instruction receive occured
	// TODO: PC will always be enabled for reading new PC
	
	///////////////////////////////////////////////////////////
	//////////////*INSTRUCTION MEMORY INTERFACE*///////////////
	///////////////////////////////////////////////////////////

	// instruction outputs to Instruction Memory
	output inst_o_valid; // TODO: Analyze
	
	assign inst_o_valid = !bubble & !pause & !reset;
/*	output bubble_debug;
	output pause_debug;
	output reset_debug;
	assign bubble_debug = bubble;
	assign pause_debug = pause;
	assign reset_debug = reset;
*/	
	// instructions inputs from Instruction Memory
	input [31:0] inst_i;
	
	///////////////////////////////////////////////////////////
	//////////////////////*HAZARD LOGIC*///////////////////////
	///////////////////////////////////////////////////////////
	
	output inst_delay_exception;
	
	wire inst_delay_timer_mid;
	FF inst_delay_timer (.clk(clk), .reset(reset), .enable(1'b1), .set(!bubble & !pause & !reset), .q(inst_delay_timer_mid));
	FF inst_delay_exception_ff (.clk(clk), .reset(reset | inst_i_valid), .enable(inst_delay_timer_mid | inst_i_valid), .set(inst_delay_timer_mid), .q(inst_delay_exception));
	
	///////////////////////////////////////////////////////////
	/////////////////////*REGISTER FILE*///////////////////////
	///////////////////////////////////////////////////////////
	
	// outputs buffered by register file
	output [31:0] pc_plus_four;
	output [31:0] instruction;
	DelayRegisterFile pc_plus_four_file (.clk(clk), .reset(reset), .pause(pause), .data(pc_plus_four_mid), .q(pc_plus_four));
	DelayRegisterFile instruction_file (.clk(clk), .reset(reset), .pause(pause), .data(inst_i), .q(instruction));

endmodule
